//	------------------------------------------------------------------------------------------------
//	File name:		PdbFile.cpp
//	Author:			Marc Ochsenmeier
//	Email:			marc@ochsenmeier.de
//	Web:			www.breakpoints.de - 
//	Date:			02.07.2009
//
//	------------------------------------------------------------------------------------------------
#include "stdafx.h"

#include "PdbTranslationMaps.h"
#include "PdbFile.h"
#include "PdbModule.h"
#include "PdbParserInternal.h"
#include "PdbModuleDetails.h"

CPdbFile::CPdbFile(CPdbParser* pPdbInterface, const std::wstring& path) :
	m_pPdbParser(pPdbInterface),
	m_pIDiaSession(NULL), 
	m_pIDiaSymbol(NULL),
	m_sFileName(path)
{
}
CPdbFile::~CPdbFile()
{
	Cleanup();
}
void CPdbFile::Close()
{
	Cleanup();
}
void CPdbFile::Cleanup()
{
	//	Clean Translation Map
	std::vector<IPdbModule*>::iterator it = m_vectorSymbols.begin();
	for( ;it!=m_vectorSymbols.end(); it++)
	{
		CPdbModule* pPdbSymbol = (CPdbModule*) *it;
		delete pPdbSymbol;
	}
	m_vectorSymbols.clear();

	if (m_pIDiaSymbol) 
	{
		m_pIDiaSymbol->Release();
		m_pIDiaSymbol = NULL;
	}
	if (m_pIDiaSession) 
	{
		m_pIDiaSession->Release();
		m_pIDiaSession = NULL;
	}
}
// Load a PDB file
bool CPdbFile::Load(const std::wstring& sFileName)
{
	bool bRet = false;
	bool bContinue = false;

	//	Extract file extension
	wchar_t wszExt[MAX_PATH];
	if(_wsplitpath_s(sFileName.c_str(), NULL, 0, NULL, 0, NULL, 0, wszExt, MAX_PATH)==0)
	{
		//	PDB File?
		if(_wcsicmp(wszExt, L".pdb")==0)
		{
			// Open (WITHOUT validation) the PDB file as a debug data source
			bContinue = LoadSymbolsFileWithoutValidation(sFileName);
		}
	}

	if(bContinue)
	{
		// Retrieve a reference to the global scope
		bRet = m_pIDiaSession->get_globalScope(&m_pIDiaSymbol)==S_OK?true:false;
	}
	return bRet;
}
bool CPdbFile::LoadSymbolsFileWithoutValidation(const std::wstring& sPdbFileName)
{
	bool bRet = false;

	//	Retrieve the DIA datasource
	IDiaDataSource* pIDiaDataSource = m_pPdbParser->GetDiaDataSource();

	if(pIDiaDataSource)
	{
		bool bContinue = false;

		//	Load the PDB file
		HRESULT hr = pIDiaDataSource->loadDataFromPdb(sPdbFileName.c_str());
		if(SUCCEEDED(hr))
		{
			bContinue = true;
		}
		else
		{
			switch(hr)
			{
				//	Failed to open the file, or determined that the file has an invalid format.
			case E_PDB_NOT_FOUND:
				break;
				//	Attempted to access a file with an obsolete format.
			case E_PDB_FORMAT:
				break;
				//	Invalid parameter.
			case E_INVALIDARG:
				break;
				//	Data source has already been prepared.
			case E_UNEXPECTED:
				bContinue = true;
				break;
			default:
				break;
			}
		}
		
		if(bContinue)
		{
			// Open a session for querying symbols
			bRet = pIDiaDataSource->openSession(&m_pIDiaSession)==S_OK?true:false;
		}
	}
	return bRet;
}
GUID CPdbFile::GetGuid() const
{
	GUID guid = GUID_NULL;
	if(m_pIDiaSymbol)
	{
		HRESULT hr = m_pIDiaSymbol->get_guid(&guid);
		if(hr==S_OK)
		{
			LPOLESTR lp = NULL;
			hr = StringFromCLSID(guid, &lp);
			if(hr==S_OK)
			{
				CoTaskMemFree(lp);
			}
		}
	}
	return guid;
}
bool CPdbFile::IsStripped()
{
	bool bRet = false;
	if(m_pIDiaSymbol)
	{
		BOOL b = FALSE;
		HRESULT hr = m_pIDiaSymbol->get_isStripped(&b);
		if(hr==S_OK)
		{
			bRet = b?true:false;
		}
	}
	return bRet;
}

std::vector<IPdbModule*>& CPdbFile::GetModules()
{
	//	Cleanup Translations Map from previous call
	std::vector<IPdbModule*>::iterator it = m_vectorSymbols.begin();
	for( ;it!=m_vectorSymbols.end(); it++)
	{
		CPdbModule* pPdbSymbol = (CPdbModule*) *it;
		delete pPdbSymbol;
	}
	m_vectorSymbols.clear();

	if(m_pIDiaSymbol)
	{
		IDiaEnumSymbols* pIDiaEnumSymbols = NULL;
		HRESULT hr = m_pIDiaSymbol->findChildren(SymTagCompiland, NULL, nsNone, &pIDiaEnumSymbols);
		if(hr==S_OK)
		{
			LONG lCount = 0;
			hr = pIDiaEnumSymbols->get_Count(&lCount);
			
			IDiaSymbol* pIDiaSymbol = NULL;
			for(DWORD dwPos=0; dwPos<(DWORD)lCount; dwPos++)
			{
				hr = pIDiaEnumSymbols->Item(dwPos, &pIDiaSymbol);
				if(hr==S_OK)
				{
					CPdbModule* pSymbol = new CPdbModule(this, pIDiaSymbol);
					
					//	Keep this symbol in the List? (e.g. Reject Symbol beginning with "Import:")
					if(pSymbol->Reject())
					{
						delete pSymbol;
						pSymbol = NULL;
					}
					else
					{
						m_vectorSymbols.push_back(pSymbol);
					}
				}
			}
			pIDiaEnumSymbols->Release();
		}
	}
	return m_vectorSymbols;
}